회고
[우아한테크코스 프리코스] 문자열 계산기 과제 회고
문자열 계산기 과제는 요구 사항 분리, 객체 지향 설계, 예외 상황 처리를 학습하는 기회였다. 이 과정에서 겪은 설계 변경, 디버깅, 환경 설정 문제를 정리했다.
1. 설계와 리팩토링: 컨트롤러 도입과 예외 처리
컨트롤러 도입을 통한 관심사 분리
초기 설계는 Application 클래스가 객체 생성과 실행 흐름을 모두 담당했다. '관심사의 분리' 원칙을 적용하기 위해, 로직 실행을 담당하는 CalculatorController를 추가로 도입했다.
트러블슈팅: assertThatThrownBy 테스트 실패
컨트롤러 run() 메서드에 try-catch 문을 배치하자, IllegalArgumentException을 검증하는 assertThatThrownBy 테스트가 실패했다.
원인: 컨트롤러의 catch 블록이 예외를 처리한 후 전파하지 않아, 테스트 코드에서 예외를 감지하지 못했다.
// 문제가 발생했던 컨트롤러의 예외 처리
public void run() {
try {
// ... 로직 실행 ...
} catch (IllegalArgumentException e) {
// 여기서 예외를 잡고 종료
OutputView.printError(e.getMessage());
}
}
해결: 에러 메시지 출력 책임은 컨트롤러에 유지하기로 했다. catch 블록에서 에러 메시지를 출력한 뒤, throw e;를 사용해 예외를 다시 던지는(re-throw) 방식으로 해결했다.
// 수정한 컨트롤러의 예외 처리
public void run() {
try {
// ... 로직 실행 ...
} catch (IllegalArgumentException e) {
OutputView.printError(e.getMessage()); // 1. 에러 메시지 출력 처리
throw e; // 2. 예외를 다시 던짐
}
}
이 방식을 적용하자 테스트가 정상 통과했다. 이 과정에서 예외 처리 흐름과 테스트 코드의 상호작용을 명확히 이해할 수 있었다.
2. 기술적 난관과 디버깅
원인 1: 유효성 검사 및 파싱 순서 오류
음수 및 숫자 판별 로직에서 순서 문제를 발견했다.
문제 코드:
// ...
String[] textNumbers = text.split(delimiter);
int[] numbers = new int[textNumbers.length]; // 1. 배열 초기화 (모든 요소 0)
if(!isPositive(numbers)) { // 2. 파싱 전 유효성 검사
throw new IllegalArgumentException("입력값에 양수가 아닌 숫자가 포함되어 있습니다.");
}
for(int i = 0; i < textNumbers.length; i++) { // 3. 실제 파싱
numbers[i] = Integer.parseInt(textNumbers[i]);
}
// ...
원인: 문자열을 int로 파싱하기 전에 유효성 검사를 먼저 수행했다. new int[size]로 생성된 배열은 0으로 초기화되며, 유효성 검사 로직이 이 0을 유효하지 않은 값으로 판단하여 예외를 발생시켰다.
해결: 파싱을 먼저 수행하고, 파싱된 숫자 배열을 대상으로 유효성 검사를 하도록 순서를 변경했다.
원인 2: \n 처리 방식 오해
커스텀 구분자 요구사항("//;\n1;2;3")에서 \n의 처리를 고민했다. 테스트 코드를 분석한 결과, \n은 개행 문자가 아닌 구분자와 숫자 문자열을 나누는 단순 **'문자열 리터럴'**로 사용된 것을 확인했다. 따라서 split("\n")으로 문자열을 분리했다.
3. 환경 문제: WSL과 Java 버전 불일치
개발 환경 설정에서 예상치 못한 문제가 발생했다.
증상: IntelliJ에서는 WSL의 Java 21 버전으로 정상 동작했으나, 터미널에서 ./gradlew clean test 실행 시 테스트가 실패했다. 터미널의 java -version은 17로 확인되었다.
원인 추적: update-alternatives로 버전을 21로 변경해도 터미널 재시작 시 17로 복귀했다. 확인 결과 ~/.bashrc 파일에 JAVA_HOME이 17 버전으로 고정되어 있었다.
해결: ~/.bashrc의 JAVA_HOME 경로를 Java 21로 수정한 뒤 설정을 적용(source ~/.bashrc)했다. 이후 터미널 버전이 21로 일치되었다.
# ~/.bashrc
# export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 (문제의 원인)
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 # (수정)
export PATH=$JAVA_HOME/bin:$PATH
4. 최종 소감
예외 처리, 테스트 코드 동작 원리, 객체지향 설계, 개발 환경 일관성까지 고민해야 했다.
특히 WSL 환경에서 IDE와 터미널의 자바 버전 불일치 문제를 직접 경험하고 해결한 것이 의미 있었다. 코드 외적인 문제 해결의 중요성을 배울 수 있었다. 사실 설계하고 환경설정하는 것이 가장 어려운 것 같다.
추가 회고
- 이번 미션에서 커스텀 에러처리 클래스, 에러 코드 등 예외처리에 대한 부분을 강화하고싶었는데 못해서 아쉽다. 다음 미션에서는 적용해볼 생각이다.
- 이번에는 테스팅 보다는 디버깅을 사용하는 것에 집중했다. 기존에 Nodejs프로젝트에서 vscode로 많이 사용했는데 개인적으로 intellij 디버깅이 훨씬 보기 좋고 편했다. 그래서 이번 미션에 더 사용하게된 것 같다.
- "덧셈할 문자열을 입력해 주세요."을 출력하는 것은 OutputView라는 클래스가 책임지는 것이 맞는데 급하게 형식에 맞춰서 출력하다가 실수를 했다. 누가 어떤 책임을 가지는지는 명확하게 다시한번 생각해볼 필요가 있는 것 같다.
- 상수값에에 대해서 전혀 생각을 못했는데 다른사람 코드리뷰를 하다가 깨달았다. 이것도 다음 미션에는 유의해서 사용해보자.
- 양수에 대한 정의를 다시 한번 찾아봤는데 양의 실수이던데... 아무 생각없이 정수로 처리를 했다. 요구사항에 맞게 작성할려면 실수로 처리를 하는게 맞을 것 같다.